package com.finance.rule.service;

import com.finance.base.enums.CalculateTypeEnum;
import com.finance.base.enums.SettlementTypeEnum;
import com.finance.report.bean.pojo.LogisticsData;
import com.finance.rule.bean.pojo.*;
import com.finance.rule.dao.JudgeRuleMapper;
import com.finance.system.bean.pojo.Organization;
import com.finance.system.bean.pojo.Region;
import com.finance.system.bean.pojo.RegionDetail;
import com.finance.system.bean.pojo.User;
import com.finance.system.service.IOrganizationService;
import com.finance.system.service.IRegionCodeService;
import com.finance.system.service.IUserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @Author: gly
 * @Date: Created in 2017/5/2
 * @Description: 判断运单对应的计费规则
 */
@Service
public class JudgeRuleService implements IJudgeRuleService {
    private Logger logger = LoggerFactory.getLogger(JudgeRuleService.class);

    @Autowired
    private JudgeRuleMapper judgeRuleMapper;

    @Autowired
    private IRegionCodeService regionCodeService;

    @Autowired
    private IOrganizationService organizationService;

    @Autowired
    private IUserService userService;


    @Override
    public List<ReturnRule> judgeRule(LogisticsData logisticsData) {
        logger.info("开始根据运单寻找按重或按件规则，{}",logisticsData.getWaybillNum());
        List<ReturnRule> returnRules = new ArrayList<ReturnRule>();
        //接单仓库所在市级编码
        this.logger.info("运单数据：{} 配送机构：{}",logisticsData.toString(),logisticsData.getOrgcodeDelivery());
        this.logger.info("接单机构编码：{} 运单信息：{}",logisticsData.getOrgcodeAccept(),logisticsData.getWaybillNum());
        Organization organizationAccept = organizationService.getByCode(logisticsData.getOrgcodeAccept());
        Organization organizationDelivery = organizationService.getByCode(logisticsData.getOrgcodeDelivery());
        if(organizationAccept == null || organizationDelivery == null){
            this.logger.info("运单{}配送机构和接单机构信息缺失",logisticsData.getWaybillNum());
            return returnRules;
        }
        String fromCityCode = organizationAccept.getCityCode();
        //配送机构所在省级编码
        String provinceCode = organizationDelivery.getProvinceCode();
        //供应商编码
        String supplierCode = logisticsData.getSupplierCode();
        //收件地址所在省级编码
        String consignProCode = logisticsData.getProvinceConsignee();
        //收件地址所在市级编码
        String consignCityCode = logisticsData.getCityConsignee();
        //收件地址所在县级编码
        String consignCountyCode = logisticsData.getCountyConsignee();
        logger.info("开始根据供货商{}和省份{}为运单{}寻找按件规则",new String[]{supplierCode,provinceCode,logisticsData.getWaybillNum()});
        if(StringUtils.isBlank(provinceCode)) {
            provinceCode = consignProCode;
        }
        //匹配按件规则
        if (!StringUtils.isBlank(provinceCode)) {
            List<String> regionCodes = new ArrayList<String>();
            regionCodes.add(provinceCode);
            if(StringUtils.isNotBlank(consignCityCode)){
                regionCodes.add(consignCityCode);
            }else {
                regionCodes.add(organizationDelivery.getCityCode());
            }
            if(StringUtils.isNotBlank(consignCountyCode)){
                regionCodes.add(consignCountyCode);
            }else{
                regionCodes.add(organizationDelivery.getCountyCode());
            }
            List<PieceRangeRule> pieceRangeRules = findPieceRules(logisticsData, regionCodes, supplierCode);
            if(CollectionUtils.isNotEmpty(pieceRangeRules)){
                ReturnRule returnRule = new ReturnRule();
                returnRule.setObject(pieceRangeRules);
                returnRule.setType(CalculateTypeEnum.CALCULATE_BY_PIECE.getValue());
                returnRules.add(returnRule);
            }else{
                logger.error("{}未匹配到任何按件规则", logisticsData.getWaybillNum());
            }
        }else {
            logger.error("{}配送机构省份及收件人省份未知 无法适配按件规则", logisticsData.getWaybillNum());
        }
        //匹配按重规则
        if (!StringUtils.isBlank(fromCityCode)){
            List<WeightRangeRule> weightRangeRules = findWeightRules(logisticsData, fromCityCode, supplierCode, consignProCode, consignCityCode, consignCountyCode);
            if(CollectionUtils.isNotEmpty(weightRangeRules)){
                ReturnRule returnRule = new ReturnRule();
                returnRule.setObject(weightRangeRules);
                returnRule.setType(CalculateTypeEnum.CALCULATE_BY_WEIGHT.getValue());
                returnRules.add(returnRule);
            }else{
                logger.error("{}未匹配到任何重规则", logisticsData.getWaybillNum());
            }
        } else {
            logger.error("{}接单机构的城市编码未知 无法适配按重规则", logisticsData.getWaybillNum());
        }
        //匹配混合规则
        List <MixSectionRule> mixSectionRules = findMixRules(logisticsData,supplierCode);
        if(CollectionUtils.isNotEmpty(mixSectionRules)){
            ReturnRule returnRule = new ReturnRule();
            returnRule.setObject(mixSectionRules);
            returnRule.setType(CalculateTypeEnum.CALCULATE_BY_MIX.getValue());
            returnRules.add(returnRule);
        }else{
            logger.error("{}未匹配到任何混合规则", logisticsData.getWaybillNum());
        }
        return returnRules;
    }

    /**
     * 匹配按重规则
     * @param logisticsData
     * @param fromCityCode
     * @param supplierCode
     * @param consignProCode
     * @param consignCityCode
     * @param consignCountyCode
     * @return
     */
    private List<WeightRangeRule> findWeightRules(LogisticsData logisticsData,String fromCityCode, String supplierCode, String consignProCode, String consignCityCode, String consignCountyCode) {
        List<WeightRangeRule> weightRangeRulesAll = judgeRuleMapper.getWeightRules(supplierCode, fromCityCode);
        logger.info("根据供货商{}和发货地{}为运单{}寻找到{}条按重规则",new String[]{supplierCode,fromCityCode,logisticsData.getWaybillNum(),weightRangeRulesAll == null ? "0" : weightRangeRulesAll.size() + ""});
        List<WeightRangeRule> weightRangeRules = new ArrayList<WeightRangeRule>();
        if(CollectionUtils.isEmpty(weightRangeRulesAll)) {
            return weightRangeRules;
        }
        if (weightRangeRulesAll != null && weightRangeRulesAll.size() > 0) {
            Iterator<WeightRangeRule> it = weightRangeRulesAll.iterator();
            while (it.hasNext()) {
                WeightRangeRule item = it.next();
                int level = item.getSettlementRegion().getToRegion().getLevel();
                String fromRegionCode = item.getSettlementRegion().getFromRegion().getCode();
                String toRegionCode = item.getSettlementRegion().getToRegion().getCode();
                if (!StringUtils.isBlank(consignCountyCode)) {
                    //判断起止点是否在规则范围内
                    if (this.inRegion(this.findAcceptRegionCode(logisticsData),fromRegionCode) && this.inRegion(this.findDeliveryRegionCode(logisticsData),toRegionCode)) {
                        this.logger.info("按重规则收货地址区域匹配成功 运单号为{}",logisticsData.getWaybillNum());
                    }else{
                        this.logger.info("{}按重规则过滤,始发地或目的地不匹配",logisticsData.getWaybillNum());
                        continue;
                    }
                    ValueRange valueRange = item.getValueRange();
                    if(valueRange != null && this.satisfyValueRange(valueRange,logisticsData.getWeight())){
                        weightRangeRules.add(item);
                        this.logger.info("按重规则重量区间匹配成功 运单号为{}",logisticsData.getWaybillNum());
                        continue;
                    }
                    if(item.getCod() != null){
                        BigDecimal cod = StringUtils.isBlank(logisticsData.getCod()) ? new BigDecimal(0) : new BigDecimal(logisticsData.getCod());
                        if(cod.compareTo(new BigDecimal(0)) >0){
                            weightRangeRules.add(item);
                        }else{
                            this.logger.info("【按件规则】代收款：{} 代收款不匹配 运单号：{} 代收款：{}",new String[]{logisticsData.getWaybillNum(),cod.toPlainString()});
                            continue;
                        }
                    }
                } else {
                    logger.error("此运单收货地址县级编码为空 运单号为{}", logisticsData.getWaybillNum());
                }
            }
        }
        logger.info("根据其他条件为运单{}筛选到{}条按重规则",new String[]{logisticsData.getWaybillNum(),weightRangeRules.size() + ""});
        return weightRangeRules;
    }

    /**
     *
     * @param valueRange
     * @param weight
     * @return
     */
    private boolean satisfyValueRange(ValueRange valueRange, BigDecimal weight){
        if(weight == null) {
            weight = new BigDecimal(0);
        }
        boolean in = true;
        if(valueRange.getContainLeft() == 1){
            in = in && valueRange.getPointLeft().compareTo(weight) <= 0;
        }else if(valueRange.getContainLeft() == 0){
            in = in && valueRange.getPointLeft().compareTo(weight) < 0;
        }
        if(valueRange.getContainRight() == 1){
            in = in && valueRange.getPointRight().compareTo(weight)>= 0;
        }else if(valueRange.getContainRight() == 0){
            in = in && valueRange.getPointRight().compareTo(weight) > 0;
        }
        return in;
    }

    private String findAcceptRegionCode(LogisticsData logisticsData){
        Organization acceptOrg = this.organizationService.getByCode(logisticsData.getOrgcodeAccept());
        String result = StringUtils.isNotBlank(acceptOrg.getCountyCode()) ? acceptOrg.getCountyCode() : StringUtils.isNotBlank(acceptOrg.getCityCode()) ? acceptOrg.getCityCode() :acceptOrg.getProvinceCode();
        return result;
    }

    private String findDeliveryRegionCode(LogisticsData logisticsData){
        Organization deliveryOrg = this.organizationService.getByCode(logisticsData.getOrgcodeDelivery());
        String result = StringUtils.isNotBlank(deliveryOrg.getCountyCode()) ? deliveryOrg.getCountyCode() : logisticsData.getCountyConsignee();
        result = StringUtils.isBlank(result) ? (StringUtils.isNotBlank(deliveryOrg.getCityCode()) ?  deliveryOrg.getCityCode():logisticsData.getCityConsignee()) : result;
        result = StringUtils.isBlank(result) ? (StringUtils.isNotBlank(deliveryOrg.getProvinceCode()) ?  deliveryOrg.getCityCode():logisticsData.getProvinceConsignee()):result;
        return result;
    }

    private List<PieceRangeRule> findPieceRules(LogisticsData logisticsData, List<String> regionCodes, String supplierCode) {
        List<PieceRangeRule> pieceRangeRules = judgeRuleMapper.getPieceRules(supplierCode, regionCodes);
        logger.info("根据供货商{}和省份{}为运单{}寻找到{}条按件规则",new String[]{supplierCode,regionCodes.toString(),logisticsData.getWaybillNum(),pieceRangeRules == null? "0" : pieceRangeRules.size() + ""});
        List<PieceRangeRule> result = new ArrayList<PieceRangeRule>();
        if(CollectionUtils.isEmpty(pieceRangeRules)) {
            return result;
        }
        result.addAll(pieceRangeRules);
        if (pieceRangeRules != null && pieceRangeRules.size() > 0) {
            Iterator<PieceRangeRule> it = pieceRangeRules.iterator();
            while (it.hasNext()) {
                PieceRangeRule item = it.next();
                //如果区分配送类型 不匹配的需去除
                if (item.getDirection() != null && logisticsData.getDirection() != null && item.getDirection().compareTo(-1) != 0) {
                    if (!item.getDirection().equals(logisticsData.getDirection())) {
                        result.remove(item);
                        this.logger.info("【按件规则】{}配送类型不匹配{}",logisticsData.getWaybillNum(),logisticsData.getDirection());
                        continue;
                    }

                }
                //如果区分大小 不匹配的需去除
                if (item.getPackageSize() != null && logisticsData.getPackageSize() != null && item.getPackageSize().compareTo(-1) != 0) {
                    if (item.getPackageSize().compareTo(logisticsData.getPackageSize()) != 0) {
                        result.remove(item);
                        this.logger.info("【按件规则】{}包裹类型不匹配{}"+logisticsData.getWaybillNum(),logisticsData.getPackageSize());
                        continue;
                    }
                }
                //如果区分温度要求 不匹配的需去除
                if (item.getTemperature() != null && item.getTemperature().compareTo(-1) != 0  && logisticsData.getTemperature() != null) {
                    if (item.getTemperature().compareTo(logisticsData.getTemperature()) != 0) {
                        result.remove(item);
                        this.logger.info("【按件规则】{}温度等级不匹配{}",logisticsData.getWaybillNum(),logisticsData.getTemperature());
                        continue;
                    }
                }
                //如果区分时效 不匹配的需去除
                if (item.getExpressScheduleType() != null && item.getExpressScheduleType().compareTo(-1) != 0 && logisticsData.getExpressScheduleType() != null) {
                    if (item.getExpressScheduleType().compareTo(logisticsData.getExpressScheduleType())!=0) {
                        result.remove(item);
                        this.logger.info("【按件规则】{}时效不匹配{}"+logisticsData.getWaybillNum(),logisticsData.getExpressScheduleType());
                        continue;
                    }

                }
                //如果区分合作类型 不匹配的需去除
                if (item.getSettlementType() != null && item.getSettlementType().compareTo(-1) != 0  && !StringUtils.isBlank(logisticsData.getPostmanNo())) {
                    User user = this.userService.getByCode(logisticsData.getPostmanNo());
                    String userSettlementType = StringUtils.isBlank(user.getSettlementType()) ? SettlementTypeEnum.DELIVERY_MAN_PRICE.getValue() + "" : user.getSettlementType();
                    if (!item.getSettlementType().toString().equals(userSettlementType)) {
                        result.remove(item);
                        this.logger.info("【按件规则】规则合作类型：{} 合作类型不匹配 运单号：{} 快递员结算类型：{}",new String[]{item.getSettlementType().toString(), logisticsData.getWaybillNum(), userSettlementType});
                        continue;
                    }
                }
                if(item.getCod() != null && item.getCod() != 0){
                    BigDecimal cod = StringUtils.isBlank(logisticsData.getCod()) ? new BigDecimal(0) : new BigDecimal(logisticsData.getCod());
                    if(cod.compareTo(new BigDecimal(0)) <= 0){
                        result.remove(item);
                        this.logger.info("【按件规则】代收款：{} 代收款不匹配 运单号：{} 代收款：{}",new String[]{item.getCod().toString(), logisticsData.getWaybillNum(), cod.toPlainString()});
                        continue;
                    }
                }
                //非指定的省份
                String source = item.getRegionDetail().getCode();
                String target = this.findDeliveryRegionCode(logisticsData);
                if(item.getRegionDetail() != null && ! this.inRegion(target,source)){
                    result.remove(item);
                    this.logger.info("【按件规则】{}省份不匹配{}",logisticsData.getWaybillNum(),logisticsData.getProvinceConsignee());
                    continue;
                }
                if(item.getSigner() != null && item.getSigner().compareTo(logisticsData.getSigner()) != 0 && item.getSigner().compareTo(-1) != 0){
                    result.remove(item);
                    this.logger.info("【按件规则】{}签收人不匹配{}Skip",logisticsData.getWaybillNum(),logisticsData.getSigner());
                    continue;
                }
            }
        }
        logger.info("根据其他条件为运单{}筛选到{}条按件规则",new String[]{logisticsData.getWaybillNum(),pieceRangeRules.size()+""});
        return result;
    }

    private List<MixSectionRule> findMixRules(LogisticsData logisticsData,String supplierCode){
        List<MixSectionRule> mixSectionRules = judgeRuleMapper.getMixRules(supplierCode);
        List<MixSectionRule> result = new ArrayList<MixSectionRule>();
        if(CollectionUtils.isEmpty(mixSectionRules)) {
            return result;
        }
        result.addAll(mixSectionRules);
        logger.info("根据供货商{}为运单{}寻找到{}条混合规则",new String[]{supplierCode,logisticsData.getWaybillNum(),mixSectionRules == null? "0" : mixSectionRules.size() + ""});
        if(mixSectionRules != null && mixSectionRules.size()>0){
            Iterator<MixSectionRule> it = mixSectionRules.iterator();
            while (it.hasNext()){
                MixSectionRule item = it.next();
                if(item.getDirection() != null && logisticsData.getDirection() != null && item.getDirection().compareTo(-1) != 0){
                    if(!item.getDirection().equals(logisticsData.getDirection())){
                        result.remove(item);
                        this.logger.info("【混合规则】配送类型不匹配[waybillNum]:{}[direction]:{}",logisticsData.getWaybillNum(),logisticsData.getDirection());
                        this.logger.info("【按件规则】配送类型不匹配 按件规则配送类型[direction]{}:",item.getDirection());
                        continue;
                    }
                }
                User postman = userService.getByCode(logisticsData.getPostmanNo());
                if(postman != null && postman.getSettlementType() != null && item.getSettlementType() != null){
                    if(!postman.getSettlementType().equals(item.getSettlementType().toString())) {
                        result.remove(item);
                        this.logger.info("【混合规则】结算类型不匹配[waybillNum]:{}[settlementType]:{}",logisticsData.getWaybillNum(),postman.getSettlementType());
                        continue;
                    }
                }
                ValueRange valueRange = item.getValueRange();
                if(valueRange != null && !this.satisfyValueRange(valueRange,logisticsData.getWeight())){
                    result.remove(item);
                    this.logger.info("【混合规则】重量区间不匹配[waybillNum]:{}[valueRange]:{}",logisticsData.getWaybillNum(),valueRange.toString());
                    continue;
                }

            }
        }
        return  result;
    }

    /**
     * 判断target是否在source内
     * @param target
     * @param source
     * @return
     */
    private boolean inRegion(String target,String source){
        RegionDetail targetRegion = this.regionCodeService.getDetailByCode(target);
        RegionDetail sourceRegion = this.regionCodeService.getDetailByCode(source);
        if(targetRegion == null) {
            return false;
        }
        boolean in = false;
        switch (sourceRegion.getLevel()){
            case Region.LEVE_PROVINCE :
                in = targetRegion.getLevel()== Region.LEVE_PROVINCE ? target.equals(source) : targetRegion.getProvinceCode().equals(source);
                break;
            case Region.LEVE_CITY :
                in = targetRegion.getLevel() == Region.LEVE_CITY ? target.equals(source) :
                        targetRegion.getLevel() == Region.LEVE_PROVINCE ? false : targetRegion.getCityCode().equals(source);
                break;
            case Region.LEVE_COUNTY :
                in = targetRegion.getLevel() == Region.LEVE_COUNTY ? target.equals(source) : false;
                break;
            default:
                break;
        }
        return in;
    }
}



